/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007-2008 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.config.autoconfiguration.web.spring.validator;

import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

import com.genia.toolbox.basics.exception.technical.TechnicalException;
import com.genia.toolbox.config.autoconfiguration.bean.ConfigurationBean;
import com.genia.toolbox.config.autoconfiguration.process.ProcessConfiguration;
import com.genia.toolbox.config.autoconfiguration.web.spring.command.ConfigurationCommand;

/**
 * The validation for the configuration form.
 */
public class ConfigurationValidator
    implements Validator
{

  /**
   * minimal size of the password.
   */
  public static final int PASSWORD_MIN_SIZE = 6;

  /**
   * Password mismatch code problem.
   */
  public static final String PASSWORD_DOES_NOT_MATCH = ConfigurationValidator.class.getName() + ".passwordDoesNotMatch";

  /**
   * Password verification mismatch code problem.
   */
  public static final String PASSWORD_CHECK_DOES_NOT_MATCH = ConfigurationValidator.class.getName() + ".passwordCheckDoesNotMatch";

  /**
   * Password is or stay the default password.
   */
  public static final String DEFAULT_PASSWORD_PROHIBITED = ConfigurationValidator.class.getName() + ".defaultPasswordProhibited";

  /**
   * the pool max size is too low.
   */
  public static final String POOL_MAX_SIZE_TOO_LOW = ConfigurationValidator.class.getName() + ".poolMaxSizeTooLow";

  /**
   * Password is not long enough.
   */
  public static final String PASSWORD_TOO_SHORT = ConfigurationValidator.class.getName() + ".passwordTooShort";



  /**
   * Can this {@link Validator} {@link #validate(Object, Errors) validate}
   * instances of the supplied <code>clazz</code>?
   * <p>
   * This method is <i>typically</i> implemented like so: <pre class="code">
   * return Foo.class.isAssignableFrom(clazz); </pre> (Where <code>Foo</code> is
   * the class (or superclass) of the actual object instance that is to be
   * {@link #validate(Object, Errors) validated}.)
   * 
   * @param clazz
   *          the {@link Class} that this {@link Validator} is being asked if it
   *          can {@link #validate(Object, Errors) validate}
   * @return <code>true</code> if this {@link Validator} can indeed
   *         {@link #validate(Object, Errors) validate} instances of the
   *         supplied <code>clazz</code>
   * @see org.springframework.validation.Validator#supports(java.lang.Class)
   */
  @SuppressWarnings("unchecked")
  public boolean supports(Class clazz)
  {
    return ConfigurationCommand.class.isAssignableFrom(clazz);
  }



  /**
   * Validate the supplied <code>target</code> object, which must be of a
   * {@link Class} for which the {@link #supports(Class)} method typically has
   * (or would) return <code>true</code>.
   * <p>
   * The supplied {@link Errors errors} instance can be used to report any
   * resulting validation errors.
   * 
   * @param target
   *          the object that is to be validated (can be <code>null</code>)
   * @param errors
   *          contextual state about the validation process (never
   *          <code>null</code>)
   * @see org.springframework.validation.ValidationUtils
   * @see org.springframework.validation.Validator#validate(java.lang.Object,
   *      org.springframework.validation.Errors)
   */
  public void validate(Object target, Errors errors)
  {
    ConfigurationCommand configurationCommand = (ConfigurationCommand) target;
    validateOldPassword(configurationCommand, errors);
    validateNewPassword(configurationCommand, errors);
  }



  /**
   * returns the {@link ConfigurationBean}, and add an error if it is not
   * retrievable.
   * 
   * @param errors
   *          the {@link Errors} to complete.
   * @return the {@link ConfigurationBean}, and add an error if it is not
   *         retrievable
   */
  private ConfigurationBean getConfigurationBean(Errors errors)
  {
    try {
      return ProcessConfiguration.getConfigurationBean();
    }
    catch (Exception e) {
      errors.reject(TechnicalException.DEFAULT_KEY, new String[] { e.getMessage() }, e.getMessage());
      return null;
    }

  }



  /**
   * validate that the new passord given is correct.
   * 
   * @param configurationCommand
   *          the object that is to be validated (can be <code>null</code>)
   * @param errors
   *          contextual state about the validation process (never
   *          <code>null</code>)
   * @return <code>true</code> if and only if the new password is correct
   */
  public boolean validateNewPassword(ConfigurationCommand configurationCommand, Errors errors)
  {
    ConfigurationBean configurationBean = getConfigurationBean(errors);
    if (configurationBean == null) {
      return false;
    }
    
    boolean doValidate = true;

    if (!ObjectUtils.nullSafeEquals(configurationCommand.getNewPassword(), configurationCommand.getNewPasswordCheck())) {
      errors.rejectValue("newPasswordCheck", PASSWORD_CHECK_DOES_NOT_MATCH);
      doValidate = false;
    }

    if (ObjectUtils.nullSafeEquals(configurationCommand.getNewPassword(), ConfigurationBean.DEFAULT_PASSWORD)) {
      errors.rejectValue("newPassword", DEFAULT_PASSWORD_PROHIBITED);
      doValidate = false;
    }

    if (!StringUtils.hasLength(configurationCommand.getNewPassword()) && ConfigurationBean.DEFAULT_PASSWORD.equals(configurationBean.getPassword())) {
      errors.rejectValue("newPassword", DEFAULT_PASSWORD_PROHIBITED);
      doValidate = false;
    }

    if (StringUtils.hasLength(configurationCommand.getNewPassword()) && configurationCommand.getNewPassword().length() < PASSWORD_MIN_SIZE) {
      errors.rejectValue("newPassword", PASSWORD_TOO_SHORT, new Object[] { PASSWORD_MIN_SIZE }, null);
      doValidate = false;
    }

    return doValidate;

  }



  /**
   * validate that the poolMaxSize given is correct.
   * 
   * @param configurationCommand
   *          the object that is to be validated (can be <code>null</code>)
   * @param errors
   *          contextual state about the validation process (never
   *          <code>null</code>)
   */
  public void validatePoolMaxSize(ConfigurationCommand configurationCommand, Errors errors)
  {
    if (configurationCommand.getPoolMaxSize() < 1) {
      errors.rejectValue("poolMaxSize", POOL_MAX_SIZE_TOO_LOW);
    }

  }



  /**
   * validate that the passord given is correct.
   * 
   * @param configurationCommand
   *          the object that is to be validated (can be <code>null</code>)
   * @param errors
   *          contextual state about the validation process (never
   *          <code>null</code>)
   * @return <code>true</code> if and only if the password given is correct
   */
  public boolean validateOldPassword(ConfigurationCommand configurationCommand, Errors errors)
  {
    ConfigurationBean configurationBean = getConfigurationBean(errors);
    if (configurationBean == null) {
      return false;
    }
    if (!ObjectUtils.nullSafeEquals(configurationCommand.getOldPassword(), configurationBean.getPassword())) {
      errors.rejectValue("oldPassword", PASSWORD_DOES_NOT_MATCH);
      return false;
    }
    return true;
  }
}
